package com.lincoln.framework;

import com.lincoln.framework.bean.AbstractDecoder;
import com.lincoln.framework.bean.AbstractEncoder;
import com.lincoln.framework.bean.AbstractProtocol;
import com.lincoln.framework.factories.ProtocolFactory;
import com.lincoln.framework.interfaces.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author lincoln
 * @describe 服务器
 * @date 2018年11月29日17:11:17
 */
public class NettyServer {
    public static final int DEFAULT_PORT = 9999;
    public static final long DEFAULT_TIMEOUT = 5 * 60;
    //=================================================================================================================
    /**
     * 单例
     */
    private static NettyServer nettyServer;

    /**
     * 启动服务器
     *
     * @throws InterruptedException
     */
    public static void runServer() throws InterruptedException {
        runServer(DEFAULT_PORT);
    }

    /**
     * 启动服务器
     *
     * @param port 设置端口号
     * @throws InterruptedException
     */
    public static void runServer(int port) {
        if (nettyServer == null) {
            return;
        }
        new Thread(() -> {
            try {
                nettyServer.run();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

    }

    public static NettyServer getNettyServer() {
        return nettyServer;
    }

    public static void setNettyServer(NettyServer nettyServer) {
        NettyServer.nettyServer = nettyServer;
    }

    /**
     * 扫描协议
     *
     * @return
     */
    public static boolean scanProtocol(int deviceType, int protocolType, String classPath) {
        return ProtocolFactory.scanProtocol(deviceType, protocolType, classPath);
    }

    /**
     * 扫描协议
     *
     * @return
     */
    public static boolean scanProtocol(String classPath) {
        return ProtocolFactory.scanProtocol(classPath);
    }

    //=================================================================================================================
    /**
     * 日志
     */
    private static Logger logger = LoggerFactory.getLogger(NettyServer.class);

    private int port;
    /**
     * 超时断开时间:单位秒
     */
    private long timeout;
    /**
     * 解码器
     */
    @Getter
    private Class<? extends Decoder> decoder;
    /**
     * 编码器
     */
    @Getter
    private Class<? extends Encoder> encoder;
    /**
     * 协议结构
     */
    @Getter
    private Class<? extends BaseProtocol> protocol;
    /**
     * 设备信息
     */
    @Getter
    private Class<? extends BaseDeviceInfo> deviceInfo;


    /**
     * 通过port构建,解码器使用默认
     */
    public NettyServer(Class<? extends BaseDeviceInfo> deviceInfo) {
        this(DEFAULT_PORT, DEFAULT_TIMEOUT, deviceInfo);
    }

    public NettyServer(int port, Class<? extends BaseDeviceInfo> deviceInfo) {
        this(port, DEFAULT_TIMEOUT, deviceInfo);
    }

    public NettyServer(long timeout, Class<? extends BaseDeviceInfo> deviceInfo) {
        this(DEFAULT_PORT, timeout, deviceInfo);
    }

    /**
     * 通过port构建,解码器使用默认
     *
     * @param port
     */
    public NettyServer(int port, long timeout, Class<? extends BaseDeviceInfo> deviceInfo) {
        this(port, timeout, AbstractDecoder.class, AbstractEncoder.class, AbstractProtocol.class, deviceInfo);
    }

    /**
     * 完全构造函数
     *
     * @param port
     * @param decoder
     * @param encoder
     */
    public NettyServer(int port, long timeout, Class<? extends Decoder> decoder, Class<? extends Encoder> encoder, Class<? extends BaseProtocol> protocol, Class<? extends BaseDeviceInfo> deviceInfo) {
        this.port = port;
        this.timeout = timeout;
        this.decoder = decoder;
        this.encoder = encoder;
        this.protocol = protocol;
        this.deviceInfo = deviceInfo;
    }

    //构造对应的类
    public NettyServer decoder(Class<? extends Decoder> decoder) {
        this.decoder = decoder;
        return this;
    }

    public NettyServer encoder(Class<? extends Encoder> encoder) {
        this.encoder = encoder;
        return this;
    }

    public NettyServer protocol(Class<? extends BaseProtocol> protocol) {
        this.protocol = protocol;
        return this;
    }

    public NettyServer deviceInfo(Class<? extends BaseDeviceInfo> deviceInfo) {
        this.deviceInfo = deviceInfo;
        return this;
    }

    public void run() throws InterruptedException {
        logger.info("服务器在" + this.port + "端口启动");
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(encoder.newInstance());
                            ch.pipeline().addLast(decoder.newInstance());
                            ch.pipeline().addLast(new IdleStateHandler(timeout, 0, 0, TimeUnit.SECONDS));
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync();
            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 6000;
        }
        NettyServer.runServer(port);
    }
}
